import plotly.express as px
import plotly.graph_objects as go
import matplotlib
from plotly.subplots import make_subplots
import plotly.io as pio
pio.renderers.default='notebook'
matplotlib.rcParams['figure.dpi'] = 300
from scipy.sparse.linalg import svds
import gensim as gs
import networkx as nx
import numpy as np
import pandas as pd
import resources
import credentials
from scraping import scrape
from processing import store_pipeline_dataset
from utils import load_dataset, top_k_sim_docs, print_top_sim_docs, docs_to_graph, network_plot, store_dataset
from topic_detection import tfidf_representation, lsi_representation, terms_filter, communities_from_graph, topic_characterization
from comments import extract_comments, filter_comments, sentiment_emotion_analysis
%load_ext autoreload
%autoreload 2
presentation_mode = True
nota bene: in fase di presentazione le operazioni onerose non verranno eseguite, per visualizzare i risultati utilizzeremo i dati in cache, impostando presentation_mode = True.
In questa fase andiamo a configurare i parametri di scraping.
profiles: profili instagram di cui fare lo scraping;user: il profilo che usiamo per accedere ad Instagram;days_ahead: quanto andare indietro nei giorni durante lo scraping;hours_offset: quante ore trascurare per evitare di recuperare post troppo recenti (quindi senza commenti).datasets_folder: cartella che conterrà i risultati grezzi dello scraping.if not presentation_mode:
profiles = resources.profiles
user = credentials.USER
datasets_folder = resources.raw_datasets_folder()
days_ahead = resources.days_ahead
hours_offset = resources.hours_offset
print(f"Scraping da: {', '.join(profiles)}")
scrape(profiles, datasets_folder, user, days_ahead, hours_offset)
In questa fase vengono presi i dataset grezzi ed eseguiamo:
Infine scriviamo il nuovo dataset su file.
if not presentation_mode:
store_pipeline_dataset(resources.raw_datasets_folder(),
resources.processed_dataset_file())
dataset = load_dataset(resources.processed_dataset_file())
In questa fase creiamo lo spazio semantico relativo ai post. Partiamo dalla rappresentazione vettoriale TF-IDF dei documenti rispetto ai termini del nostro dizionario. Eseguiamo LSI sulla matrice documenti/termini, prendendo le prime 80 dimensioni principali.
idx2id = {idx: id for idx, id in enumerate(dataset.keys())}
corpus_tokens = [value['tokens'].split(' ') for value in dataset.values()]
corpus_dict = gs.corpora.Dictionary(corpus_tokens)
corpus_bow = [corpus_dict.doc2bow(tokens) for tokens in corpus_tokens]
corpus_tfidf = tfidf_representation(corpus_bow)
corpus_lsi_mtx = lsi_representation(corpus_tfidf, corpus_dict, 80)
corpus_caption = [value['caption'] for value in dataset.values()]
print_top_sim_docs(corpus_lsi_mtx, 237, corpus_caption, 5)
!237! ----------- 1.000000238418579 Continuano le provocazioni in mare tra Nato e Russia. Stavolta ad alzare la tensione è la Marina russa che prende nuovamente di mira il nostro Paese. L'incrociatore "Varyag", il gemello della "Moskva" affondato nel Mar Nero dagli ucraini, sta navigando pericolosamente al largo delle coste pugliesi. ... !50! ----------- 0.8079158663749695 L'incrociatore russo "Varyag", gemello del " Moksva", avanza nel Mar Ionio, al largo delle coste pugliesi, all'altezza di Santa Maria di Leuca, scortato da un caccia, un'altra nave di supporto, la "Ammiraglio Tributs", che lo protegge da ipotetici attacchi aerei o di sottomarini. Lo si apprende da s... !253! ----------- 0.717470645904541 Il primo ponte stradale fra la Russia e la Cina è stato inaugurato, come riferisce l'agenzia di stampa russa Ria Novosti. La struttura collega la città russa di Blagoveshchensk con quella di Heihe in territorio cinese, sul fiume Amur. Secondo quanto riportato dalle agenzie russe è aperto al traffico... !265! ----------- 0.6671116948127747 "Alla fine l'Ue ha perso la sovranità politica". Così il presidente russo Vladimir Putin alla plenaria del forum economico di San Pietroburgo. "Le sue elite stanno ballando alla musica di qualcun altro", "danneggiando la loro popolazione" e "i veri interessi" degli europei "vengono messi da parte". ... !775! ----------- 0.6021993160247803 La Russia "non cadrà nella stessa trappola dell'Urss, la sua economia resterà aperta". Lo ha detto Vladimir Putin parlando ai giovani imprenditori russi. Aggiungendo che la sfida che oggi ha di fronte il Paese è "tornare e rafforzarsi". "Non avevamo un'economia chiusa. Bene, l'avevamo in epoca sovi...
In questa fase otteniamo il network dei post. I nodi rappresentano i post, tra due post c'è un link se hanno similarità coseno superiore alla soglia stabilita (0.65). Nel plot, ogni nodo ha un'etichetta dove vengono mostrati le sue prime 10 parole chiave, cioè i termini con più alto valore di tf-idf che non sono verbo o aggettivi.
G = docs_to_graph(corpus_lsi_mtx, 0.65)
# Label da mostrare nel grafo
idx2graph_label = []
for terms_id in corpus_tfidf:
# ordiniamo i termini per valore di tf-idf in ordine decrescente
terms = [corpus_dict[term[0]]for term in sorted(terms_id, key=lambda x: x[1], reverse=True)]
# eliminiamo verbi ed aggettivi
terms = terms_filter(terms)
# prendiamo i primi 10 termini
idx2graph_label.append(terms[:10])
idx2label = ['<br>'.join([f'<b>{idx}</b>'] + kws) for idx, kws in enumerate(idx2graph_label)]
# visualizzazione del grafo
network_plot(G, nx.fruchterman_reingold_layout(G), idx2label)
Abbiamo deciso di estrapolare i topic attraverso l'algoritmo di community detection di Louvain sul grafo ottenuto precedentemente. Manteniamo solo le comunità che contengono più di tre post, per assicurarci che un topic sia effettivamente trattato in un certo numero di post.
Eseguiamo una fase di community characterization al fine di estrarre i termini più rappresentativi per ogni comunità. Per fare ciò abbiamo assegnato ad ogni comunità un documento dato dalla concatenazione di dei documenti dei suoi post. Successivamente abbiamo eseguito il TF-IDF su questo nuovo insieme di documenti.
Infine, memorizziamo le comunità ottenute su un file.
if not presentation_mode:
communities = communities_from_graph(G, members_threshold=3)
topics = topic_characterization(corpus_tokens, communities, corpus_dict, idx2id, num_kw=15)
store_dataset(topics, resources.topics_file())
Di seguito la visualizzazione dei primi 4 topic con i rispettivi numeri di post e termini.
topics = load_dataset(resources.topics_file())
for id, topic in enumerate(topics[:]):
print(f"Topic {id} - n. post: {len(topic['posts'])}\n{' '.join(topic['terms'])}", end="\n\n")
Topic 0 - n. post: 5 ovetto negozio benjamin lettera chiaruzzi danilo kinder alimentari svizzera caramelle cartolina rispondare patatine san 12enne Topic 1 - n. post: 17 mascherine mascherina obbligo ministri obbligatorio esami trasporto aerei mezzi sottosegretario luoghi teatri cinema maturità settembre Topic 2 - n. post: 14 russia putin vladimir guerra interessi ucraina pietro bacio mariupol mosca zar ikea pietroburgo villa urss Topic 3 - n. post: 22 siccità acqua idrico agricoltura fiume desertificazione crisi padano piogge irrigazione lago plastica temperature osservatorio bacino Topic 4 - n. post: 6 salario salari direttiva produttività metà giovani lavoratori euro generazioneeuropa mediano ottimale tridico cifra soglia misura Topic 5 - n. post: 27 macron scholz ucraina kiev draghi visita zelensky cancelliere ucraino presidente emmanuel treno olaf leader guerra Topic 6 - n. post: 4 gigante tartaruga galapagos fernanda museo tartarughe tropiquarium isola secolo albino servion zoo chelonoidis esemplari ricerche Topic 7 - n. post: 8 spread inflazione prezzi famiglie aumento debito banca tassi economia consumatori vacanze bastare maggio titoli riparo Topic 8 - n. post: 11 cervello virus sintomi cerebrale long temperatura omicron cov2 sars studio covid memoria infezione alzheimer debilitanto Topic 9 - n. post: 16 coscioni mario suicidio associazione carboni luca federico assistire assistito farmaco medicalmente strumentazione cappato dicessare barca Topic 10 - n. post: 4 bar caffè scontrino banco euro macchiatone venezia padovana prezzi consumazione gestore macchiatoni rialto bancone rivo Topic 11 - n. post: 17 catania elena patti madre rapimento martina carabinieri mascalucia elenadelpozzo cadavere bambina madriassassine martinapatti pozzo ritrovamento Topic 12 - n. post: 5 explorer microsoft browser internet windows edge internetexplorer ie versioni firefox sistemi mercato recente web chrome Topic 13 - n. post: 7 m5s conte movimento maio giuseppe luigidimaio scissione luigi fico giuseppeconte dimaio movimento5stelle beppegrillo stelle camera Topic 14 - n. post: 10 bonus lavoratori aiuti decreto indennità euro dipendenti disoccupazione evasione erogazione datore tasse lavoratore busta buste Topic 15 - n. post: 5 cesare cremonini stadi cesarecremonini show cremoninare imola lignano concerto getty stadio date milano palco data Topic 16 - n. post: 9 ridolfi sedazione fabio suicidio fermignano 46enne urbino assistire pesaro medicalmente coscioni assistito comitato fabioridolfi oculare Topic 17 - n. post: 5 test gravidanza concorso prova comuni vigili sindacato candidata requisito torre vigone commissario esposto pellice atletica Topic 18 - n. post: 24 referendum quesito quorum giustizia quesiti voto abrogativo urne referendario elezioni elettori affluenza comuni partecipazione magistrati Topic 19 - n. post: 6 centrosinistra elezioni centrodestra turno verona ballottaggio palermo comuni capoluoghi catanzaro genova spoglio aquila vantaggio coalizioni Topic 20 - n. post: 5 luna fragole superluna cielo satellite nomi york new orbita perigeo istanbul infiorescenze lune raccolta occhi Topic 21 - n. post: 8 fedez psicologo tumore audio pancreas rapper diagnosi figli seduta malattia devastarmi piango ferragni pensiero paura Topic 22 - n. post: 16 pride elodie madrina corteo roma manifestazione zingaretti cantante diritti capitale lazio nicola carri gualtiero mieli Topic 23 - n. post: 5 violenza campovolo rcf arena emma eliso pausini concerto giorgia reggio protagoniste unanessunacentomila mannoia emilia alessandra Topic 24 - n. post: 4 mcdonald russia s ristoranti catena fast govor food logo proprietario gid mcdonalds alexandre mantearr pbo Topic 25 - n. post: 9 hack astrofisica margherita statua milano scienziata scultura scienza fondazione margheritahack deloitto richini stelle statale suolo Topic 26 - n. post: 4 lettura copertina autore libri racconti battagliafm incipit scrittura edition edicola anri cercas digital geografia jacobs Topic 27 - n. post: 5 ospedaliero sanità visite pandemia letto salute bolzano degenza regioni posti cura abitanti autonomo servizi assistenziale Topic 28 - n. post: 6 euro centesimi litro lettini benzina prezzi ombrelloni self medio prezzo tariffe rincari media lidi gasolio Topic 29 - n. post: 6 paralisi bieber sindrome volto esercizi hunt nervi orecchio ramsay viso justin virus justinbieber occhio nervo Topic 30 - n. post: 13 amarcord compleanno televisione auguri jurassicpark spielberg steven marchionne nadia nadiatoffa toffa ballo successi luglio giugno Topic 31 - n. post: 8 der leyen von ursula ue commissione ucraina presidente status unione direttiva europea moldova membri udienza Topic 32 - n. post: 8 auto diesel benzina emissioni emendamento ppe proposta vendita industria automobilistico co2 europarlamento deroga produzione commissione Topic 33 - n. post: 13 senatrice segro memoriale ferragni shoah liliano segre chiaraferragni chiacchierate telefonico determinazione influencer fedez lilianasegre incontro Topic 34 - n. post: 4 bieber justin toronto malattia lyme tour mononucleosi date justice scotiabank arena fan justinbieber covid19 popstar Topic 35 - n. post: 8 mieloma allevi giovanni pianista insidioso neoplasia perdonatemi compositore girere suono fedez angoscia interiore pensiero dovrò Topic 36 - n. post: 6 incendi spagna incendio ettari sviluppatosi rogo castiglia culebra sierra zamora vigili fiamme fuoco interrotta parco Topic 37 - n. post: 9 pride diritti lgbt famiglia omosessualità comunità torino gay lgbtq libertà stampa identità zan posizione giornale Topic 38 - n. post: 4 bici biden joe delaware rehoboth beach clip pedali scarpe secret service agenti presidente rialzato reporter Topic 39 - n. post: 5 rifugiati unhcr polonia stiller ambasciatore commissariato confine benstiller ucraina ucraino guerra solidarietà km leopoli shevchenko Topic 40 - n. post: 15 torneo berrettino atp tennis berrettini krajinovic queen matteo erba stoccarda tennista s filip set infortunio Topic 41 - n. post: 8 sanremo amadeus festival ferragni tg1 conduttore conduttrice chiaraferragni serata imprenditrice febbraio gemellaggio condurrà direttore annuncio Topic 42 - n. post: 6 napoli pizzeria lda patagonia scampio gigi plebiscito esposito polo ospito rire ii coppie sabrina eterosessuali Topic 43 - n. post: 4 rahul india pozzo ossigeno sahu cortile telecamera minuto profondità artesiano conduttura salvataggio chhattisgarth pihrid serpente Topic 44 - n. post: 4 tartarughe caretta nidi florida tartaruga wwf liuto juno beach reti iucn orientale natura mare coste Topic 45 - n. post: 4 principe william trono carlo louis ascot corona charlotte kensington palace regno neil elisabetta impegni erede Topic 46 - n. post: 5 palermo b playoff sezioni padova presidenti promozione rosanero seggi seggio serie caos igor petyx scrutatori Topic 47 - n. post: 4 kim napalm phuc guerra bambina nudo fotografo phan thi times vietnam papà nick bambini ucraina Topic 48 - n. post: 4 sofia argento cerchio ginnastica palla azzurra boryano clavette kaleyn raffaelo medaglia atamanov aviv ritmica tel Topic 49 - n. post: 4 lopez emme jennifer pronome jlo figlia angeles christina gala perri years los dodgers maribel microfono Topic 50 - n. post: 8 edoardo oro ceccon medaglia avversario gara budapest mondiali thomas dorso kickboxing vasca rana ryan tavolino Topic 51 - n. post: 4 strasburgo giorgi denunce silvia corte marito degradante sentenza magistrati inumano figli donna diritti istruttoria inascoltare Topic 52 - n. post: 4 castità matrimonio laici virtù documento dicastero coppie dio sposo amicizia mentalità preparazione vaticano giovani linee Topic 53 - n. post: 5 amber johnny depp heard asta dollari tok processo nobel amberheard diffamazione guthrie nbc savannah einstein Topic 54 - n. post: 6 armi democratici intesa sicurezza bipartisan senatori semiautomatico fondi senato tavolo strage texas uvalde bonino ikea Topic 55 - n. post: 5 alopecia smith farmaco will capelli autoimmune baricitinib fda jada pinkett inibitori spray leah trattamento farmaci Topic 56 - n. post: 8 cloe transfobia cloebianco insegnante blog abiti segreteria fisica treviso studenti trans camper donà mattei piave Topic 57 - n. post: 5 coming styles out bandiera wembley mattia cartello baseball blair harry gay fan testa aiutami league Topic 58 - n. post: 4 sesso transgender fina identità categoria genere federazione regolamento élite pubertà rowling nuoto donna gare transizione Topic 59 - n. post: 4 elettra riccione scemo set lamborghini dj accaduto bare elettralamborghini istante cantante parolina sfigati cogl hater Topic 60 - n. post: 4 gnonto mancini willy gol calcio baveno stage ct azzurri wilfried azzurro papà liceo maturità partita Topic 61 - n. post: 4 game netflix reality squid snow partecipanti vincitore serie concorrenti jon gioco premio riprese stagione piattaforma Topic 62 - n. post: 4 tesi dedica giulia notti bari esame laurea università lettere ce studenti bocciatura perrone fallimento ansia Topic 63 - n. post: 9 maturità beppesevergnini prova tracce studenti attualità maturandi tema guerinoni maturità2022 scuolaitaliana schettini consigli esame scuolazoo Topic 64 - n. post: 4 lamda google lemoine ingegnere artificiale intelligenza conversazioni lemoino blake chatbot openai senziente ia coscienza esistenza Topic 65 - n. post: 4 pizza briatore crazy chilo cliente euro flavio pizze prezzi costo napoli insalate mozzarella ingredienti qualità Topic 66 - n. post: 8 stipendio euro reddito figlio mese rdc stipendi contratti sussidio saraceno redditodicittadinanza assegno spesa figlia impiego Topic 67 - n. post: 4 aborto donne gravidanza neet età sí ministra informazioni cura fascia montero accaniscere yellen bugia ministero Topic 68 - n. post: 12 bologna diritti cucchi idee repidee maurizio molinari repubblica repidee2022 ilario workshop est bilico fiera town Topic 69 - n. post: 6 uvalde drag bambini strage queen karol sparatoria madre texas scuola amerie illianare eroi stress usa
Prima dell'analisi eliminiamo i commenti che:
appartengono a community eliminate precedentemente.
patterns_to_remove contiene espressioni regolari di pattern che non vogliamo mantenere, ad esempio le citazioni.
spam_patterns contiene espressioni regolari riscontrate in commenti di spam, ad esempio '@gary_mary_fx'.if not presentation_mode:
patterns_to_remove = resources.comm_patterns_to_remove
spam_patterns = resources.comm_spam_patterns
# COMMENTS OF THE KEPT POSTS
commns_char = load_dataset(resources.topics_file())
posts_to_keep = [post for c in commns_char for post in c['posts']]
comm_spam_patterns = resources.comm_spam_patterns
comm_patterns_to_remove = resources.comm_patterns_to_remove
comments = extract_comments(dataset, posts_to_keep)
filtered_comments = filter_comments(comments, comm_spam_patterns, comm_patterns_to_remove)
store_dataset(filtered_comments, resources.filtered_comments_file())
Grazie ad un modello basato su rete neurale già allenato, facciamo sentiment e emotion analysis dei testi dei commenti.
if not presentation_mode:
analyzed_comments = sentiment_emotion_analysis(filtered_comments)
store_dataset(analyzed_comments, resources.analyzed_comments_file())
Dalle precedenti analisi possiamo ottenere una matrice utente/topic con 1 se l'utente ha commentato positivamente il post, -1 se ha commentato negativamente, 0 altrimenti.
Facciamo la decomposizione SVD della matrice ottenendo del relativi matrici u, s e v.
topic_dataset = load_dataset(resources.topics_file())
analyzed_comments = load_dataset(resources.analyzed_comments_file())
topic2terms = {i: topic['terms'] for i, topic in enumerate(topic_dataset)}
post2topic = {post: i for i, topic in enumerate(topic_dataset) for post in topic['posts']}
user_topic = [(comm['owner'], post2topic[comm['post']], comm['sentiment'])
for comm in analyzed_comments
if 'post' in comm]
df = pd.DataFrame(user_topic, columns=['user', 'topic', 'sentiment'])
df = df.assign(sentiment=df.sentiment.apply(lambda x: -1 if x == 'negative' else 1))
df = df.groupby(['user', 'topic']).sentiment.sum().reset_index()
df = df.pivot(index='user', columns='topic', values='sentiment')
df.fillna(0, inplace=True)
matrix = df.values
u_mtx, s_mtx, v_mtx_t = svds(matrix, k=10)
res = (u_mtx @ np.diag(s_mtx) @ v_mtx_t)
Grazie alla matrice v possiamo individuare i topic simili in base al comportamento dei commentatori.
test_topic = 24
elems = top_k_sim_docs(v_mtx_t.T, test_topic, k=5, with_sims=True)
for e, s in zip(*elems):
print(f"Topic: {e} - parole chiave: {topic2terms[e]}, similarità: {s}", end='\n\n')
Topic: 24 - parole chiave: ['mcdonald', 'russia', 's', 'ristoranti', 'catena', 'fast', 'govor', 'food', 'logo', 'proprietario', 'gid', 'mcdonalds', 'alexandre', 'mantearr', 'pbo'], similarità: 1.0 Topic: 13 - parole chiave: ['m5s', 'conte', 'movimento', 'maio', 'giuseppe', 'luigidimaio', 'scissione', 'luigi', 'fico', 'giuseppeconte', 'dimaio', 'movimento5stelle', 'beppegrillo', 'stelle', 'camera'], similarità: 0.9366566793697937 Topic: 16 - parole chiave: ['ridolfi', 'sedazione', 'fabio', 'suicidio', 'fermignano', '46enne', 'urbino', 'assistire', 'pesaro', 'medicalmente', 'coscioni', 'assistito', 'comitato', 'fabioridolfi', 'oculare'], similarità: 0.9334636421985563 Topic: 52 - parole chiave: ['castità', 'matrimonio', 'laici', 'virtù', 'documento', 'dicastero', 'coppie', 'dio', 'sposo', 'amicizia', 'mentalità', 'preparazione', 'vaticano', 'giovani', 'linee'], similarità: 0.9211020272651718 Topic: 2 - parole chiave: ['russia', 'putin', 'vladimir', 'guerra', 'interessi', 'ucraina', 'pietro', 'bacio', 'mariupol', 'mosca', 'zar', 'ikea', 'pietroburgo', 'villa', 'urss'], similarità: 0.8558704292777225
Creiamo un grafo per rappresentare i topic, fra due topic vi è un link se la loro similarità coseno nello spazio definito prima è al di sopra di una certa soglia. Inoltre, per ogni nodo non aggiungiamo più di 5 link per non appesantire la visualizzazione.
edges = []
threshold = 0.7
for i in range(0, len(topic2terms)):
elems = top_k_sim_docs(v_mtx_t.T, i, k=5, with_sims=True)
for e, s in zip(*elems):
if (s > threshold):
edges.append((i, e, s))
G = nx.Graph()
G.add_weighted_edges_from(edges)
idx2graph_label = ['<br>'.join([f'<b>{key}</b>'] + value)
for key, value in topic2terms.items()]
network_plot(G, nx.kamada_kawai_layout(G), idx2graph_label)
Dall'analisi dei commenti andiamo a ricavare quelli che sono i topic che hanno suscitato più interesse. Calcoliamo per ogni topic il numero mediano di commenti per i post che gli appartengono e il numero di post che contiene.
post2profile = {id: post['profile'] for id, post in dataset.items()}
idx2labels = ['<br>' + ',<br>'.join(topics[x]['terms']) for x in range(len(topics))]
post2topic = {post: i for i, topic in enumerate(topics) for post in topic['posts']}
df = pd.DataFrame(analyzed_comments)
df['topic'] = df.post.apply(lambda x: post2topic[x] if x in post2topic.keys() else None)
hot_topics = df.groupby(['topic', 'post']).count().text.reset_index()
hot_topics['profile'] = hot_topics.post.apply(lambda x: post2profile[x])
hot_topics = hot_topics.groupby(['topic', 'profile']).mean()
hot_topics = hot_topics.groupby('topic').median().sort_values('text', ascending=False).reset_index()
hot_topics['terms'] = hot_topics.topic.apply(lambda x: idx2labels[x])
hot_topics['profiles'] = hot_topics.topic.apply(lambda x: {post2profile[p] for p in topics[x]['posts']})
hot_topics['num posts'] = hot_topics.topic.apply(lambda x: len(topics[x]['posts']))
hot_topics = hot_topics.rename(columns={'text': 'median comments'})
fig = px.bar(hot_topics, x='median comments', hover_data=['topic', 'terms'], color='num posts', color_continuous_scale='viridis')
fig['layout']['yaxis']['autorange'] = "reversed"
fig.show()
Abbiamo ricavato, in termini relativi, il sentiment per ogni topic a partire dai sentiment relativi ai commenti dei post.
df2 = df.groupby(['topic', 'emotion', 'sentiment']).post.count().reset_index()
sentimentDF = df2.groupby(['topic', 'sentiment']).sum().reset_index()
sentimentDF = sentimentDF.pivot(index='topic', columns=['sentiment']).droplevel(level=0, axis=1).reset_index().fillna(0)
columns = ['positive', 'negative']
sentimentDF_norm = sentimentDF[columns].div(sentimentDF[columns].sum(axis=1), axis=0)
px.bar(sentimentDF_norm[columns], barmode='group')
to_plot = sentimentDF_norm.reset_index().rename(
columns={'index': 'topic'}).sort_values('negative', ascending=True).reset_index()
to_plot['terms'] = to_plot.topic.apply(lambda x: idx2labels[x])
px.bar(to_plot[['topic', 'positive', 'negative', 'terms']], y=[
'negative', 'positive'], hover_data=['topic', 'terms'])
Abbiamo ricavato, in termini relativi, le emotion per ogni topic a partire dalle emotion relative ai post.
sorted_by = 'anger'
emotionDF = df2.groupby(['topic', 'emotion']).sum().reset_index()
emotionDF = emotionDF.pivot(index='topic', columns=['emotion']).droplevel(
level=0, axis=1).reset_index().fillna(0)
emotionDF.astype({'anger': 'int', 'fear': 'int', 'joy': 'int', 'sadness': 'int'})
columns = ['anger', 'fear', 'joy', 'sadness']
emotionDF_norm = emotionDF[columns].div(emotionDF[columns].sum(axis=1), axis=0)
to_plot = emotionDF_norm.reset_index().rename(columns={'index': 'topic'})
to_plot['terms'] = to_plot.topic.apply(lambda x: idx2labels[x])
to_plot = to_plot.sort_values(sorted_by, ascending=True).reset_index()
fig = make_subplots(rows=2, cols=2, subplot_titles=("Anger", "Fear", "Joy", "Sadness"))
fig1 = px.bar(to_plot[['anger', 'topic', 'terms']],hover_data=['topic', 'terms'])
for t in fig1.data:
fig.add_trace(t, row=1, col=1)
fig2 = px.bar(to_plot[['fear', 'topic', 'terms']], hover_data=['topic', 'terms'])
for t in fig2.data:
fig.add_trace(t, row=1, col=2)
fig3 = px.bar(to_plot[['joy', 'topic', 'terms']],hover_data=['topic', 'terms'])
for t in fig3.data:
fig.add_trace(t, row=2, col=1)
fig4 = px.bar(to_plot[['sadness', 'topic', 'terms']],hover_data=['topic', 'terms'])
for t in fig4.data:
fig.add_trace(t, row=2, col=2)
fig.layout.update(showlegend=False)
fig
Abbiamo rappresentato le distribuzioni dei sentiment, dato un topic, rispetto ai profili che hanno pubblicato post su quel topic. L'obiettivo è evidenziare le differenti reazioni in base al bacino di utenza dei profili.
# Berrettini = 40, Pozzo = 11, Segre Ferragni = 33
test_topic = 33
specific_topic = df.copy()
specific_topic['profiles'] = specific_topic.post.apply(lambda x: post2profile[x])
specific_topic = specific_topic.explode('profiles')
specific_topic = specific_topic.groupby(['profiles', 'topic', 'sentiment']).count().text.reset_index()
specific_topic = specific_topic[specific_topic.topic == test_topic]
print(', '.join(topic2terms[test_topic]))
fig = make_subplots(rows=1, cols=2, subplot_titles=("Count", "Normalized"))
fig1 = px.bar(specific_topic, y='text', x='profiles', color= 'sentiment', barmode='group')
specific_topic['total'] = specific_topic.profiles.apply(lambda x: specific_topic[specific_topic.profiles == x].text.sum())
specific_topic['text'] = specific_topic.text.div(specific_topic.total, axis=0)
# pivot the dataframe to put 'text' and 'normalized' in the column 'res'
fig2 = px.bar(specific_topic, y='text', x='profiles', color= 'sentiment')
for t in fig1.data:
fig.add_trace(go.Bar(t, showlegend=True), row=1, col=1)
for t in fig2.data:
fig.add_trace(go.Bar(t, showlegend=False), row=1, col=2)
fig.layout.update(showlegend=True)
fig
senatrice, segro, memoriale, ferragni, shoah, liliano, segre, chiaraferragni, chiacchierate, telefonico, determinazione, influencer, fedez, lilianasegre, incontro
Per valutare la precision e la recall del sistema abbiamo preso in considerazione un solo topic (per ragioni di tempo). Abbiamo optato per un topic con un considerevole numero di post. Il topic in questione è il numero 11, che tratta dell'omicidio di Elena Del Pozzo, dove sono presenti 17 documenti.
test_topic = 11
topic_terms = topics[test_topic]['terms']
list_posts = topic_dataset[test_topic]['posts']
print(f"Parole chiave: {topic_terms}, n. post: {len(list_posts)}", end='\n\n')
for post in list_posts:
print(f"{post}\n{dataset[post]['caption'][:300]} \n-----------------")
Parole chiave: ['catania', 'elena', 'patti', 'madre', 'rapimento', 'martina', 'carabinieri', 'mascalucia', 'elenadelpozzo', 'cadavere', 'bambina', 'madriassassine', 'martinapatti', 'pozzo', 'ritrovamento'], n. post: 17 Ce027PBKb8R La maestra di #Elena racconta l’ultima volta che ha visto la bambina. La piccola di 5 anni era scomparsa il 13 giugno a #Catania: a far ritrovare il corpo della piccola è stata la madre, Martina Patti. La donna, di 23 anni, ha confessato a carabinieri e Procura l'omicidio ----------------- Cex7y_zOwio È stato trovato il cadavere di Elena, la bambina di cinque anni rapita ieri a #TremestieriEtneo. Carabinieri del comando provinciale di #Catania si stanno recando sul posto. Da quanto apprende, è stata la madre a fare trovare il corpo di Elena. La donna è uscita poco fa dalla sua casa di Mascalucia, ----------------- Ce0fhyrqFYd Cos'è successo ad Elena, uccisa dalla madre a Catania. Le coltellate e il corpo occultato, ma non è ancora chiaro il movente #ANSA ----------------- CeylzCiqHaa E' stata la madre ad uccidere Elena, la bimba di 5 anni di cui ieri aveva denunciato il rapimento nel Catanese. E' stata la stessa donna, Martina Patti, 23 anni, a confessarlo ai Carabinieri nel corso di un interrogatorio. Avrebbe ucciso in casa a Mascalucia la figlia per poi portarne il corpicino i ----------------- CeyxVsZtt4o Il padre di Elena, il 24enne Alessandro Nicodemo Del Pozzo, si è subito recato sul luogo dove è stato ritrovato il corpo senza vita della bimba. Disperato è stato consolato dai parenti. L'uomo era stato arrestato per rapina nel 2020, ma poi assolto. Lui e la madre di Elena, Martina Patti, non stavan ----------------- Cex6ay7Nh0s E' stato trovato il cadavere di Elena, la bambina di cinque anni rapita ieri a Tremestieri etneo. Carabinieri del comando provinciale di Catania si sono recati sul posto. Il ritrovamento è stato possibile grazie alle "pressioni esercitate durante gli interrogatori" dagli investigatori. La notizia de ----------------- Ce3sOdUtt-o (✏️ Massimo Gramellini, "Non è Medea") Pur con tutta la delicatezza che la vicenda richiede, vorrei provare a staccare un paio di etichette appiccicate alla madre assassina di Catania. La prima è quella di «madre snaturata». Non ho mai sentito definire «snaturato» uno dei tanti padri che uccidono ----------------- Cex_h_YDvyS (✏️ Alfio Sciacca) È stato trovato il cadavere di Elena Del Pozzo, la bambina di cinque anni di cui era stato denunciato il rapimento ieri a Tremestieri etneo, Catania. Carabinieri del comando provinciale di Catania si stanno recando sul posto. Il corpo sarebbe stato fatto trovare dalla giovane madr ----------------- CeyXto2qc1v È stato trovato il cadavere di Elena Del Pozzo, la bambina di cinque anni di cui era stata denunciata ieri la #scomparsa a Mascalucia, in provincia di #Catania. A indicare il luogo dove trovare il corpo è stata la madre, Martina Patti, 23 anni, che ieri aveva denunciato di essere stata aggredita da ----------------- Ce06cOyDDPj Negli ultimi 20 anni l'opinione pubblica ha dovuto fare i conti con un’innaturale consapevolezza: le madri possono uccidere i loro figli. Non chiamatela follia! L'analisi di @anna__vagli #elenadelpozzo #martinapatti #catania #madriassassine #cronacanera ----------------- Ce0vBgcjTIy Elena Del Pozzo che corre felice tra le braccia della mamma all'uscita dall'asilo. Sono queste le ultime, drammatiche, immagini della bambina di 5 anni che solo qualche minuto più tardi verrà uccisa dalla mamma, Martina Patti, 24 anni, a coltellate. Il filmato, ripreso dalle telecamere di sorveglia ----------------- CeyvmvLjvJp Cosa spinge una madre a inscenare il rapimento di una figlia? Dal punto di vista criminologico la risposta è semplice. Da quello logico, invece, è incomprensibile. L'analisi di @anna__vagli #elenadelpozzo #martinapatti #catania #madriassassine #cronacanera ----------------- Ceyl6nAID-u Il cadavere della piccola Elena è stato trovato in un campo incolto vicino casa a Mascalucia, nel Catanese. A indicare il luogo in cui si trovava il corpicino senza vita della bimba – come confermato dal Procuratore – è stata la mamma di Elena, la 23enne Martina Patti, che è stata portata via dai ca ----------------- CeyghJNM49G Per un bambino la casa è il posto più sicuro al mondo. Oppure il più pericoloso. E questo ce lo dimostra la cronaca. Negli ultimi vent’anni l’opinione pubblica ha dovuto fare i conti con un’innaturale consapevolezza: le madri possono togliere la vita ai loro figli. Proprio le stesse donne che avreb ----------------- CeyDVRAtJai 🔴È stata la madre, Martina Patti, dopo un interrogatorio durato un'intera notte, a indicare il luogo in cui gli inquirenti avrebbero potuto ritrovare morta la figlia, Elena Del Pozzo. “Il suo cadavere è nascosto in un campo”, le parole della donna che ha così fatto crollare il castello di bugie c ----------------- Ce_fdHMjKpE Elena del Pozzo è stata uccisa con almeno undici coltellate. Sono questi i primissimi risultati emersi dall'autopsia effettuata ieri all'ospedale Cannizzaro di Catania sul corpo della bambina di 5 anni, scomparsa e poi trovata cadavere nei giorni scorsi. Ad ucciderla è stata la mamma Martina Patti ----------------- CfBrrUCD1yX #elenadelpozzo #martinapatti #catania #madriassassine #cronacanera hé era pazza 👆🏻 -----------------
Abbiamo valutato tutti i documenti all'interno del topic come pertinenti.
#parlano del topic 11 e sono stati presi correttamente
true_positive = {'Ce027PBKb8R', 'Cex7y_zOwio', 'Ce0fhyrqFYd','CeylzCiqHaa',
'CeyxVsZtt4o', 'Cex6ay7Nh0s', 'Ce3sOdUtt-o', 'Cex_h_YDvyS',
'CeyXto2qc1v', 'Ce06cOyDDPj', 'Ce0vBgcjTIy', 'CeyvmvLjvJp',
'Ceyl6nAID-u', 'CeyghJNM49G', 'CeyDVRAtJai', 'Ce_fdHMjKpE',
'CfBrrUCD1yX'}
# non parlano del topic 11 e sono stati presi erroneamente
false_positive = []
Successivamente, siamo andati alla ricerca di altri documenti che parlassero dello stesso argomento, ma che non fossero presenti nel topic. Per circoscrivere l'attività di ricerca abbiamo considerato solo i post che contenessero almeno uno tra i termini 'elena', 'martina', 'catania' e 'cadavere'.
Abbiamo ottenuto due ulteriori documenti che citano l'argomento in esame.
# parlano del topic 11 ma non sono stati presi
false_negative = []
captions_to_verify = []
for idx, doc in enumerate(corpus_tokens):
if set(doc).intersection({'elena', 'martina', 'catania', 'cadavere'}):
captions_to_verify.append((idx2id[idx], ' '.join(doc)))
#print(captions_to_verify, len(captions_to_verify))
positive = {'Ce027PBKb8R', 'Cex7y_zOwio', 'Ce0fhyrqFYd', 'CeylzCiqHaa',
'CeyxVsZtt4o', 'Cex6ay7Nh0s', 'Ce-yZMxsHVn', 'Ce0z3WlKZ39', 'Ce3sOdUtt-o',
'Cex_h_YDvyS', 'CeyXto2qc1v', 'Ce06cOyDDPj', 'Ce0vBgcjTIy', 'CeyvmvLjvJp',
'Ceyl6nAID-u', 'CeyghJNM49G', 'CeyDVRAtJai', 'Ce_fdHMjKpE', 'CfBrrUCD1yX'}
false_negative = positive - true_positive
false_negative, len(false_negative)
({'Ce-yZMxsHVn', 'Ce0z3WlKZ39'}, 2)
n_true_negative = len(corpus_tokens) - len(positive)
precision = len(true_positive) / (len(true_positive) + len(false_positive))
recall = len(true_positive) / (len(true_positive) + len(false_negative))
precision, recall
(1.0, 0.8947368421052632)